# -*- coding: utf-8 -*-
"""
Wargame AI的“上层AI” —— 用于比赛的第2阶段想定2010211129
2020-06
"""
__author__ = 'Yu XH'


import copy
from myAI.hexmap.handle_map import *


class FactionAI:
    def __init__(self, myAgent):
        self.color = myAgent.color
        self.scenario = myAgent.scenario
        self.max_step = 1800
        self.task = {}  # 公共变量，保存算子分配的任务信息{obj_id:['outputfire',param1,param2]}
        self.tactic = None  # 我方策略，'1-attack' | '2-defend'
        self.group_info = []  # 保存算子分组信息 [{group_id: 1, group_task: {ToOccupy:city}, operators: [opr1, opr2}]
        self.task_copy = {}  # factionAI运行中的操作对象，作用是减少对self.task的操作
        self.init_deploy_flag = True  # 开局部署兵力的标志
        self.last_observation = myAgent.scenario_info[-1]  # 保存上一步状态，便于和当前状态比较


    def reset(self):
        self.color = None
        self.scenario = None
        self.max_step = 1800
        self.task = {}
        self.tactic = None
        self.group_info = None
        self.task_copy = {}  # factionAI运行中的操作对象，作用是减少对self.task的操作
        self.last_observation = None  # 保存上一步状态，便于和当前状态比较


    def update(self, myAgent):
        # 读取任务
        myAgent.agenda.ipcArgs.mutex_task.acquire()
        self.task_copy = copy.deepcopy(self.task)
        myAgent.agenda.ipcArgs.mutex_task.release()

        # 如果我方有算子死亡，则将其任务删除
        our_oprs = myAgent.situations.our_oprs
        our_oprs_id = [opr['obj_id'] for opr in our_oprs]
        need_pop = [opr_id for opr_id in self.task_copy.keys() if opr_id not in our_oprs_id]
        for pop_id in need_pop:
            self.task_copy.pop(pop_id)
        # 如果我方算子有无效任务（Success, Failure, Error等），将任务删除
        our_oprs = myAgent.situations.our_oprs
        for opr in our_oprs:
            opr_id = opr['obj_id']
            if opr_id in self.task_copy.keys():
                if self.task_copy[opr_id][0] in ['Success', 'Failure', 'Error']:
                    self.task_copy.pop(opr_id)

        if not our_oprs:
            self.task_copy = {}
        elif self.color == 0:  # 红方策略
            self.develop_red_strategies(myAgent)
        else:  # 蓝方策略
            self.develop_blue_strategies(myAgent)

        # 写入任务
        myAgent.agenda.ipcArgs.mutex_task.acquire()
        self.task = copy.deepcopy(self.task_copy)
        myAgent.agenda.ipcArgs.mutex_task.release()

        # 更新上一次态势信息
        self.last_observation = myAgent.observation

    def develop_red_strategies(self, myAgent):
        """为红方制定策略"""
        operators = {
            'tk_id': 0,
            'ifv_id': 100,
            'inf_id': 200,
            'ucv_id': 400,
            'cm_1_id': 700,
            'cm_2_id': 701
        }
        if self.init_deploy_flag:  # 开局部署策略
            self.assign_red_init_deployments(operators, myAgent)
        elif self.check_launch_2nd_cm(operators, myAgent):  # 发射第2个巡飞弹
            # 在第1个巡飞弹结束任务后，迅速发射第2个巡飞弹。发射条件是：
            # 1. 当前天上没有巡飞弹
            # 2. 战车仍幸存
            # 3. 战车上有未发射的巡飞弹
            cm_id = operators['cm_2_id']
            ifv_id = operators['ifv_id']
            cm = myAgent.get_bop(cm_id)
            if cm:
                self.task_copy[cm_id] = ['cruise_missile']
                self.task_copy[ifv_id] = ['unload', cm_id]
        elif self.check_red_defend(operators, myAgent):  # 防守任务的判断
            # 任务条件是：
            # 1. 至少有一个夺控点我方的
            # 2. 如果2个夺控点都是我方的，防守
            # 3. 如果我方净胜分高，防守
            self.assign_red_defend_tasks(myAgent)
        elif self.check_red_control(operators, myAgent):  # 夺控任务分配
            # 红方将在最后关头实施夺控任务，夺控需要的最短时间放宽100s。夺控任务的条件是：
            # 1. 敌方无兵力或车辆算子
            # 2. 到了即将结束的阶段
            self.assign_red_control_tasks(operators, myAgent)
        else:
            # 1. 战车任务
            if self.check_red_load_infantry(operators, myAgent):  # 装载步兵任务的判断
                # 任务条件：
                # 1. 第一个巡飞弹下车中
                ifv_id, inf_id = operators['ifv_id'], operators['inf_id']
                self.task_copy[ifv_id] = ['load', inf_id]
            elif self.check_red_unload_infantry(operators, myAgent):  # 卸载步兵任务的判断
                # 任务条件：
                # 1. 步兵刚上车
                ifv_id, inf_id = operators['ifv_id'], operators['inf_id']
                self.task_copy[ifv_id] = ['unload', [inf_id]]
                self.task_copy[inf_id] = ['ambush2nd', 5437, False]
            elif self.check_red_ifv_ambush(operators, myAgent):  # 步兵下车后，对战车分派“伏击”任务，引导射击
                # 战车的前期任务是让巡飞弹、无人战车下车，以及运输步兵到下车地点
                # 在运输完毕之后，到指定地点实施“伏击”任务。分派“伏击”任务的条件：
                # 1. 步兵算子已下车
                # 2. 步兵或无人战车UCV算子实施“伏击”任务——有进行引导射击的算子
                our_oprs = myAgent.situations.our_oprs
                ifv_id = None
                for opr in our_oprs:
                    if opr['sub_type'] == BopType.IFV:
                        ifv_id = opr['obj_id']  # 找出战车算子
                        break
                if ifv_id:
                    self.task_copy[ifv_id] = ['ambush2nd', 5439, False]
            elif self.check_red_fire_propulsion_ifv(operators, myAgent):  # 战车的火力推进任务
                # 任务条件：
                # 1. 敌方有幸存算子
                # 2. 1000s内打伏击，1000s以上有无人战车的引导射击实施火力支援任务
                ifv_id = operators['ifv_id']
                self.task_copy[ifv_id] = ['fire_propulsion']
            elif self.check_red_support_firepower(operators, myAgent):  # 战车的火力支援任务
                # 任务条件：
                # 1. 1000s以上
                # 2. 无人战车算子存活
                # 3. 战车算子可以实施引导射击
                self.assign_red_support_firepower(operators, myAgent)
            # 2. 坦克任务
            if self.check_red_fire_propulsion_tank(operators, myAgent):  # 为坦克分派“火力推进”任务
                # 任务条件是：
                # 1. 敌方有幸存算子
                # 2. “伏击”任务在800s结束，或坦克导弹消耗完毕结束，转为“火力推进”任务
                tk_id = operators['tk_id']
                self.task_copy[tk_id] = ['fire_propulsion']
            # 3. 无人战车任务
            if self.check_red_guide_fire_ucv(operators, myAgent):  # 为UCV分派“引导射击”任务
                # 任务条件是：
                # 1. 850s以上
                # 2. 有条件实施引导射击
                ucv_id = operators['ucv_id']
                self.task_copy[ucv_id] = ['guide_fire']
            if self.check_red_fire_propulsion_ucv(operators, myAgent): # 为UCV分派“火力推进”任务
                # 任务条件是：
                # 1. 850s以上
                ucv_id = operators['ucv_id']
                self.task_copy[ucv_id] = ['fire_propulsion']

    def develop_blue_strategies(self, myAgent):
        """为蓝方制定策略"""
        operators = {
            'tk_id': 10000,
            'ifv_1_id': 10100,
            'ifv_2_id': 10101,
            'inf_1_id': 10200,
            'inf_2_id': 10201
        }
        if self.init_deploy_flag:  # 开局部署策略
            self.assign_blue_init_deployments(operators, myAgent)
        elif self.check_blue_anti_control(myAgent):  # 反夺控任务的判断
            # 任务条件：
            # 1. 我方成功实施了夺控行动，夺控点的所属方发生了变化
            self.assign_blue_anti_control_tasks(myAgent)
        elif self.check_blue_control(myAgent):  # 夺控任务的判断
            # 任务条件：
            # 1. 敌方算子全部消灭
            # 2. 夺控后距离结束不大于100s
            self.assign_blue_control_tasks(myAgent)
        else:
            # === 战车1算子（3426）的任务分派 ===
            if self.check_blue_deliver_infantry_1(operators, myAgent):  # 战车1执行“输送”任务的判断
                # 任务条件：
                # 1. 步兵刚上车
                ifv_1_id, inf_1_id = operators['ifv_1_id'], operators['inf_1_id']
                self.task_copy[ifv_1_id] = ['deliver', inf_1_id, 4130]
                # self.task_copy[inf_1_id] = ['control', 4435]
            elif self.check_blue_ambush_ifv_1(operators, myAgent):  # 战车1执行“伏击”任务的判断
                ifv_id = operators['ifv_1_id']
                ifv = myAgent.get_bop(ifv_id)
                if ifv:
                    self.task_copy[ifv_id] = ['ambush2nd', 4130, True]
            # === 步兵1（3426）的任务分派 ===
            if self.check_blue_control_infantry_1(operators, myAgent):  # 步兵1执行“夺控”任务的判断
                inf_1_id = operators['inf_1_id']
                self.task_copy[inf_1_id] = ['control', 4435]
            # === 战车2算子（3526）的任务分派 ===
            if self.check_blue_deliver_infantry_2(operators, myAgent):  # 战车2执行“运输”任务的判断
                # 任务条件：
                # 1. 步兵刚上车
                ifv_2_id, inf_2_id = operators['ifv_2_id'], operators['inf_2_id']
                self.task_copy[ifv_2_id] = ['deliver', inf_2_id, 4027]
                # self.task_copy[inf_2_id] = ['ambush2nd', 4126, False]
            elif self.check_blue_ambush_ifv_2(operators, myAgent):  # 战车1执行“伏击”任务的判断
                ifv_id = operators['ifv_2_id']
                ifv = myAgent.get_bop(ifv_id)
                if ifv:
                    self.task_copy[ifv_id] = ['ambush2nd', 4027, False]
            # === 步兵2（3526）的任务分派 ===
            if self.check_blue_ambush_infantry_2(operators, myAgent):  # 步兵2执行“伏击”任务的判断
                inf_2_id = operators['inf_2_id']
                self.task_copy[inf_2_id] = ['ambush2nd', 4126, False]

    def assign_blue_init_deployments(self, operators, myAgent):
        # 1. 为坦克部署“伏击”任务
        tk_id = operators['tk_id']
        tank = myAgent.get_bop(tk_id)
        if tank:
            self.task_copy[tk_id] = ['ambush2nd', 4031, True]
        # 2. 为战车1（3426）部署“装载”任务
        ifv_1_id = operators['ifv_1_id']
        ifv_1 = myAgent.get_bop(ifv_1_id)
        inf_1_id = operators['inf_1_id']
        inf_1 = myAgent.get_bop(inf_1_id)
        if ifv_1 and inf_1:
            self.task_copy[ifv_1_id] = ['load', inf_1_id]
        # 3. 为战车2（3526）部署“装载”任务
        ifv_2_id = operators['ifv_2_id']
        ifv_2 = myAgent.get_bop(ifv_2_id)
        inf_2_id = operators['inf_2_id']
        inf_2 = myAgent.get_bop(inf_2_id)
        if ifv_2 and inf_2:
            self.task_copy[ifv_2_id] = ['load', inf_2_id]
        # 修改“初始部署”标志
        self.init_deploy_flag = False

    def assign_red_init_deployments(self, operators, myAgent):
        tk_id = operators['tk_id']
        self.task_copy[tk_id] = ['ambush2nd', 5249, True]
        ucv_id = operators['ucv_id']
        self.task_copy[ucv_id] = ['ambush2nd', 5150, True]
        ifv_id = operators['ifv_id']
        cm_id = operators['cm_1_id']
        self.task_copy[ifv_id] = ['unload', [cm_id]]
        self.task_copy[cm_id] = ['cruise_missile']
        # 修改“初始部署”标志
        self.init_deploy_flag = False

    def assign_blue_control_tasks(self, myAgent):
        """为蓝方分配夺控任务"""
        our_oprs = copy.deepcopy(myAgent.situations.our_oprs)
        cities = copy.deepcopy(myAgent.observation['cities'])
        # 分派夺控任务
        map_size = myAgent.get_map_size()
        while our_oprs and cities:
            min_dist = 10000
            best_opr, best_city = {}, {}
            for opr in our_oprs:
                opr_loc = mapid_2_matind(opr['cur_hex'], map_size)
                if opr['type'] == BopType.Vehicle:
                    cost_matrix = myAgent.my_map['cost_matrix'][0]
                else:
                    cost_matrix = myAgent.my_map['cost_matrix'][1]
                for city in cities:
                    city_loc = mapid_2_matind(city['coord'], map_size)
                    cur_dist = cost_matrix[opr_loc, city_loc]
                    if cur_dist < min_dist:
                        min_dist = cur_dist
                        best_opr, best_city = opr, city
            if best_opr and best_city:
                if best_city['flag'] != self.color:
                    self.task_copy[best_opr['obj_id']] = ['control', best_city['coord']]
                else:
                    self.task_copy[best_opr['obj_id']] = ['defend_firepower', best_city['coord'], []]
            our_oprs.remove(best_opr)
            cities.remove(best_city)
        # 为剩余算子分派“防守”任务
        if our_oprs:
            # 计算针对每个夺控点的敌方算子方向
            cities = myAgent.observation['cities']
            city_locs = np.array([city['coord'] for city in cities])
            city_locs_ind = mapid_2_matind(city_locs, map_size)
            enemies = myAgent.situations.enemies
            enemy_directions = {}
            for city_loc in city_locs:
                enemy_directions[city_loc] = []
                if enemies:
                    for enemy_info in enemies:
                        if city_loc == enemy_info['operator']['cur_hex']:
                            enemy_directions[city_loc] = list(range(6))
                        else:
                            dir = get_direction(city_loc, enemy_info['operator']['cur_hex'])
                            if dir not in enemy_directions[city_loc]:
                                enemy_directions[city_loc].append(dir)
            # 找出最近的夺控点
            for opr in our_oprs:
                opr_loc_ind = mapid_2_matind(opr['cur_hex'], map_size)
                if opr['type'] == BopType.Vehicle:
                    dist_2_cities = myAgent.my_map['cost_matrix'][0][opr_loc_ind, city_locs_ind]
                else:
                    dist_2_cities = myAgent.my_map['cost_matrix'][1][opr_loc_ind, city_locs_ind]
                closest_city_loc = city_locs[np.argmin(dist_2_cities)]
                self.task_copy[opr['obj_id']] = ['defend_firepower', closest_city_loc,
                                                 enemy_directions[closest_city_loc]]

    def assign_red_defend_tasks(self, myAgent):
        """为红方分配防守任务"""
        cost_matrix = myAgent.my_map['cost_matrix'][0]
        map_size = myAgent.get_map_size()
        city_1_loc, city_2_loc = 4435, 4029
        city_1_loc_ind, city_2_loc_ind = mapid_2_matind(city_1_loc, map_size), mapid_2_matind(city_2_loc, map_size)
        city_1, city_2 = myAgent.get_city(city_1_loc), myAgent.get_city(city_2_loc)
        defend_group_1, defend_group_2 = {}, {}
        for opr in myAgent.situations.our_oprs:
            if opr['type'] == BopType.Vehicle:
                opr_loc_ind = mapid_2_matind(opr['cur_hex'], map_size)
                if city_1['flag'] == self.color:
                    dist_1 = cost_matrix[city_1_loc_ind, opr_loc_ind]
                else:
                    dist_1 = 10000
                if city_2['flag'] == self.color:
                    dist_2 = cost_matrix[city_2_loc_ind, opr_loc_ind]
                else:
                    dist_2 = 10000
                if dist_1 <= dist_2:
                    defend_group_1[opr['obj_id']] = dist_1
                else:
                    defend_group_2[opr['obj_id']] = dist_2
        if defend_group_1 and defend_group_2:
            directions_1 = get_enemy_directions(city_1_loc, myAgent.situations.enemies)
            directions_2 = get_enemy_directions(city_2_loc, myAgent.situations.enemies)
            min_dist_1 = min(defend_group_1.values())
            min_dist_2 = min(defend_group_2.values())
            ctrl_1_flag, ctrl_2_flag = True, True
            for obj_id, dist in defend_group_1.items():
                if abs(dist - min_dist_1) < 1e-8 and ctrl_1_flag:
                    self.task_copy[obj_id] = ['defend_firepower', city_1_loc, []]  # 反夺控
                    ctrl_1_flag = False
                else:
                    self.task_copy[obj_id] = ['defend_firepower', city_1_loc, directions_1]
            for obj_id, dist in defend_group_2.items():
                if abs(dist - min_dist_2) < 1e-8 and ctrl_2_flag:
                    self.task_copy[obj_id] = ['defend_firepower', city_2_loc, []]  # 反夺控
                    ctrl_2_flag = False
                else:
                    self.task_copy[obj_id] = ['defend_firepower', city_2_loc, directions_2]
        elif not defend_group_1:
            min_dist_2 = min(defend_group_2.values())
            if city_1['flag'] != self.color or len(defend_group_2) <= 1:
                pass
            else:
                min_dist_1 = 1000
                best_obj_id = None
                first_one_flag = False
                for obj_id, dist in defend_group_2.items():
                    if dist != min_dist_2 or first_one_flag:
                        obj = myAgent.get_bop(obj_id)
                        obj_loc_ind = mapid_2_matind(obj['cur_hex'], map_size)
                        cur_dist = cost_matrix[city_1_loc_ind, obj_loc_ind]
                        if min_dist_1 > cur_dist:
                            min_dist_1 = cur_dist
                            best_obj_id = obj_id
                    else:
                        first_one_flag = True
                defend_group_2.pop(best_obj_id)
                self.task_copy[best_obj_id] = ['defend_firepower', city_1_loc, []]  # 反夺控
            directions_2 = get_enemy_directions(city_2_loc, myAgent.situations.enemies)
            ctrl_2_flag = True
            for obj_id, dist in defend_group_2.items():
                if abs(dist - min_dist_2) < 1e-8 and ctrl_2_flag:
                    self.task_copy[obj_id] = ['defend_firepower', city_2_loc, []]  # 反夺控
                    ctrl_2_flag = False
                else:
                    self.task_copy[obj_id] = ['defend_firepower', city_2_loc, directions_2]
        elif not defend_group_2:
            min_dist_1 = min(defend_group_1.values())
            if city_2['flag'] != self.color or len(defend_group_1) <= 1:
                pass
            else:
                min_dist_2 = 1000
                best_obj_id = None
                first_one_flag = False
                for obj_id, dist in defend_group_1.items():
                    if dist != min_dist_1 or first_one_flag:
                        obj = myAgent.get_bop(obj_id)
                        obj_loc_ind = mapid_2_matind(obj['cur_hex'], map_size)
                        cur_dist = cost_matrix[city_2_loc_ind, obj_loc_ind]
                        if min_dist_2 > cur_dist:
                            min_dist_2 = cur_dist
                            best_obj_id = obj_id
                    else:
                        first_one_flag = True
                defend_group_1.pop(best_obj_id)
                self.task_copy[best_obj_id] = ['defend_firepower', city_2_loc, []]  # 反夺控
            directions_1 = get_enemy_directions(city_1_loc, myAgent.situations.enemies)
            ctrl_1_flag = True
            for obj_id, dist in defend_group_1.items():
                if abs(dist - min_dist_1) < 1e-8 and ctrl_1_flag:
                    self.task_copy[obj_id] = ['defend_firepower', city_1_loc, []]  # 反夺控
                    ctrl_1_flag = False
                else:
                    self.task_copy[obj_id] = ['defend_firepower', city_1_loc, directions_1]

    def assign_red_control_tasks(self, operators, myAgent):
        """为红方分配夺控任务"""
        cost_matrix = myAgent.my_map['cost_matrix'][0]
        dist_matrix = myAgent.my_map['dist_matrix']
        map_size = myAgent.get_map_size()
        enemies = myAgent.situations.enemies
        # 确定夺控对象
        city_1_loc, city_2_loc = 4435, 4029
        city_1_loc_ind, city_2_loc_ind = mapid_2_matind(city_1_loc, map_size), mapid_2_matind(city_2_loc, map_size)
        city_1, city_2 = myAgent.get_city(city_1_loc), myAgent.get_city(city_2_loc)
        if city_1['flag'] == self.color:
            tar_city = city_2
        elif city_2['flag'] == self.color:
            tar_city = city_1
        else:
            city_1_neighbor_enemy, city_2_neighbor_enemy = False, False  # 夺控点周边是否有敌方反夺控的判断
            for enemy_info in enemies:
                enemy_loc = enemy_info['operator']['cur_hex']
                enemy_loc_ind = mapid_2_matind(enemy_loc, map_size)
                if dist_matrix[city_1_loc_ind, enemy_loc_ind] <= 1:
                    city_1_neighbor_enemy = True
                if dist_matrix[city_2_loc_ind, enemy_loc_ind] <= 1:
                    city_2_neighbor_enemy = True
            if city_1_neighbor_enemy and not city_2_neighbor_enemy:
                tar_city = city_2
            elif not city_1_neighbor_enemy and city_2_neighbor_enemy:
                tar_city = city_1
            else:
                min_dist_1, min_dist_2 = 1000, 1000
                our_oprs = myAgent.situations.our_oprs
                for opr in our_oprs:
                    if opr['type'] == BopType.Vehicle:
                        opr_loc_ind = mapid_2_matind(opr['cur_hex'], map_size)
                        cur_dist_1 = cost_matrix[city_1_loc_ind, opr_loc_ind]
                        if min_dist_1 > cur_dist_1:
                            min_dist_1 = cur_dist_1
                        cur_dist_2 = cost_matrix[city_2_loc_ind, opr_loc_ind]
                        if min_dist_2 > cur_dist_2:
                            min_dist_2 = cur_dist_2
                tar_city = city_1 if min_dist_1 <= min_dist_2 else city_2
        # 确定夺控主体
        ifv_id, tk_id, ucv_id = operators['ifv_id'], operators['tk_id'], operators['ucv_id']
        ifv, tk, ucv = myAgent.get_bop(ifv_id), myAgent.get_bop(tk_id), myAgent.get_bop(ucv_id)
        ctrl_opr_id = None
        dist = 1000
        tar_city_loc_ind = mapid_2_matind(tar_city['coord'], map_size)
        if tk:
            tk_loc_ind = mapid_2_matind(tk['cur_hex'], map_size)
            move_cost = cost_matrix[tk_loc_ind, tar_city_loc_ind]
            if move_cost < dist:
                dist = move_cost
                ctrl_opr_id = tk_id
        if ifv:
            ifv_loc_ind = mapid_2_matind(ifv['cur_hex'], map_size)
            move_cost = cost_matrix[ifv_loc_ind, tar_city_loc_ind]
            if move_cost < dist:
                dist = move_cost
                ctrl_opr_id = ifv_id
        if ucv:
            ucv_loc_ind = mapid_2_matind(ucv['cur_hex'], map_size)
            move_cost = cost_matrix[ucv_loc_ind, tar_city_loc_ind]
            if move_cost < dist:
                dist = move_cost
                ctrl_opr_id = ucv_id
        self.task_copy[ctrl_opr_id] = ['control', tar_city['coord']]
        # 为其它算子分配火力输出任务
        candidates = []
        our_oprs = myAgent.situations.our_oprs
        for opr in our_oprs:
            if opr['type'] == BopType.Vehicle:
                candidates.append(opr)
        if len(candidates) > 1:
            tar_area = []
            tar_city_loc = tar_city['coord']
            tar_city_loc_ind = mapid_2_matind(tar_city_loc, map_size)
            for enemy_info in enemies:
                enemy_loc = enemy_info['operator']['cur_hex']
                enemy_loc_ind = mapid_2_matind(enemy_loc, map_size)
                if dist_matrix[tar_city_loc_ind, enemy_loc_ind] <= 5:
                    tar_area.append(enemy_loc)
            if tar_area:
                for opr in candidates:
                    opr_id = opr['obj_id']
                    if opr_id != ctrl_opr_id:
                        self.task_copy[opr_id] = ['output_firepower', tar_area]
            else:
                for opr in candidates:
                    opr_id = opr['obj_id']
                    if opr_id != ctrl_opr_id:
                        self.task_copy[opr_id] = ['defend_firepower', tar_city_loc, [3, 4, 5]]

    def assign_blue_anti_control_tasks(self, myAgent):
        """为蓝方分派“反夺控”任务"""
        last_observation = self.last_observation
        our_oprs = myAgent.situations.our_oprs
        for last_city in last_observation['cities']:
            if last_city['flag'] != self.color:
                city = myAgent.get_city(last_city['coord'])
                if city['flag'] == self.color:  # 成功夺控
                    # 找到距离该夺控点最近的算子
                    min_dist = 10000
                    closest_opr = {}
                    for opr in our_oprs:
                        if opr['type'] == BopType.Aircraft:
                            continue
                        cur_dist = myAgent.map.get_distance(city['coord'], opr['cur_hex'])
                        if cur_dist < min_dist:
                            min_dist = cur_dist
                            closest_opr = opr
                    if min_dist <= 3:
                        if closest_opr:
                            self.task_copy[closest_opr['obj_id']] = ['defend_firepower', city['coord'], []]

    def assign_red_targeted_kill_task_tank(self, myAgent):
        """为红方的坦克和无人战车分派“定点清除”任务"""
        map_size = myAgent.get_map_size()
        n_hexes = map_size['row'] * map_size['col']
        cost_matrix = myAgent.my_map['cost_matrix'][0]
        # 找出坦克算子
        our_oprs = myAgent.situations.our_oprs
        tk_obj, ucv_obj = None, None
        for opr in our_oprs:
            if opr['sub_type'] == BopType.Tank:
                tk_obj = opr  # 找出坦克算子
            if opr['sub_type'] == BopType.UCV:
                ucv_obj = opr  # 找出无人战车
        if tk_obj:
            refer_obj = tk_obj
        else:
            refer_obj = ucv_obj
        refer_loc = refer_obj['cur_hex']
        refer_loc_ind = mapid_2_matind(refer_loc, map_size)
        # === 选择定点清除的敌方算子 ===
        enemies = myAgent.situations.enemies
        n_enemies = len(enemies)
        shooted_ranges = np.zeros((n_enemies, n_hexes), dtype=bool)
        # 确定敌方的受攻击区域
        least_dists = np.ones(n_enemies, dtype=float) * 10000
        for m in range(n_enemies):
            enemy_info = enemies[m]
            enemy = enemy_info['operator']
            this_range = get_enemy_shooted_range(enemy, refer_obj, myAgent)
            shooted_ranges[m, this_range] = True
            least_dists[m] = np.min(cost_matrix[refer_loc_ind, this_range])
        # 确定敌方的独有受攻击区域
        least_unique_dists = np.ones(n_enemies, dtype=float) * 10000
        existing_flag = False
        for m in range(n_enemies):
            this_shooted_ranges = copy.deepcopy(shooted_ranges)
            this_shooted_ranges[m, :] = np.logical_not(this_shooted_ranges[m, :])
            unique_range_bool = np.all(np.logical_not(this_shooted_ranges), axis=0)
            if np.any(unique_range_bool):
                least_unique_dists[m] = np.min(cost_matrix[refer_loc_ind, unique_range_bool])
                existing_flag = True
        if existing_flag:
            tar_enemy = enemies[np.argmin(least_unique_dists)]['operator']
        else:
            tar_enemy = enemies[np.argmin(least_dists)]['operator']
        if tk_obj:
            self.task_copy[tk_obj['obj_id']] = ['targeted_kill', tar_enemy['obj_id']]
        if ucv_obj:
            self.task_copy[ucv_obj['obj_id']] = ['targeted_kill', tar_enemy['obj_id']]

    def assign_red_targeted_kill_task_ifv(self, myAgent):
        """为红方的战车分派“定点清除”任务"""
        map_size = myAgent.get_map_size()
        n_hexes = map_size['row'] * map_size['col']
        cost_matrix = myAgent.my_map['cost_matrix'][0]
        # 找出坦克算子
        our_oprs = myAgent.situations.our_oprs
        ifv = None
        for opr in our_oprs:
            if opr['sub_type'] == BopType.Tank:
                ifv = opr  # 找出坦克算子
            break
        ifv_loc = ifv['cur_hex']
        ifv_loc_ind = mapid_2_matind(ifv_loc, map_size)
        # === 选择定点清除的敌方算子 ===
        enemies = myAgent.situations.enemies
        n_enemies = len(enemies)
        shooted_ranges = np.zeros((n_enemies, n_hexes), dtype=bool)
        # 确定敌方的受攻击区域
        least_dists = np.ones(n_enemies, dtype=float) * 10000
        for m in range(n_enemies):
            enemy_info = enemies[m]
            enemy = enemy_info['operator']
            this_range = get_enemy_shooted_range(enemy, ifv, myAgent)
            shooted_ranges[m, this_range] = True
            least_dists[m] = np.min(cost_matrix[ifv_loc_ind, this_range])
        # 确定敌方的独有受攻击区域
        least_unique_dists = np.ones(n_enemies, dtype=float) * 10000
        existing_flag = False
        for m in range(n_enemies):
            this_shooted_ranges = copy.deepcopy(shooted_ranges)
            this_shooted_ranges[m, :] = np.logical_not(this_shooted_ranges[m, :])
            unique_range_bool = np.all(np.logical_not(this_shooted_ranges), axis=0)
            if np.any(unique_range_bool):
                least_unique_dists[m] = np.min(cost_matrix[ifv_loc_ind, unique_range_bool])
                existing_flag = True
        if existing_flag:
            tar_enemy = enemies[np.argmin(least_unique_dists)]['operator']
        else:
            tar_enemy = enemies[np.argmin(least_dists)]['operator']
        self.task_copy[ifv['obj_id']] = ['targeted_kill', tar_enemy['obj_id']]

    def assign_red_support_firepower(self, operators, myAgent):
        """为红方战车分派火力支援任务"""
        # 找出战车算子
        ifv_id = operators['ifv_id']
        # 确定目标区域
        enemies = myAgent.situations.enemies
        tar_area = []
        for enemy_info in enemies:
            tar_area.append(enemy_info['operator']['cur_hex'])
        if tar_area:
            self.task_copy[ifv_id] = ['support_firepower', tar_area]

    def check_blue_control(self, myAgent):
        """蓝方判断是否实施夺控任务"""
        # 如果没有需要夺控的夺控点
        cities = copy.deepcopy(myAgent.observation['cities'])
        no_city_flag = True
        for city in cities:
            if city['flag'] != self.color:
                no_city_flag = False
        if no_city_flag:
            return False
        # 如果有算子正在实施夺控任务
        for task in self.task_copy.values():
            if task[0] == 'control':
                return False
        our_oprs = copy.deepcopy(myAgent.situations.our_oprs)
        if not our_oprs:  # 没有可用的夺控算子
            return False
        # 敌方没有车辆算子，直接夺控
        no_vehicle = True
        for enemy_info in myAgent.situations.enemies:
            enemy = enemy_info['operator']
            if enemy['type'] == BopType.Vehicle:
                no_vehicle = False
        if no_vehicle:
            return True
        # 计算当前态势下，夺控需要用的最短时间
        map_size = myAgent.get_map_size()
        move_cost = 0
        while our_oprs and cities:
            min_dist = 10000
            best_opr, best_city = {}, {}
            for opr in our_oprs:
                opr_loc = mapid_2_matind(opr['cur_hex'], map_size)
                if opr['type'] == BopType.Vehicle:
                    cost_matrix = myAgent.my_map['cost_matrix'][0]
                else:
                    cost_matrix = myAgent.my_map['cost_matrix'][1]
                for city in cities:
                    city_loc = mapid_2_matind(city['coord'], map_size)
                    cur_dist = cost_matrix[opr_loc, city_loc]
                    if cur_dist < min_dist:
                        min_dist = cur_dist
                        best_opr, best_city = opr, city
            our_oprs.remove(best_opr)
            cities.remove(best_city)
            move_cost = min_dist
        # 是否到夺控时间的判断
        cur_step = myAgent.observation['time']['cur_step']
        if cur_step >= self.max_step - move_cost - 100:  # 100是松弛数值
            return True

    def check_red_defend(self, operators, myAgent):
        """红方判断是否实施防守任务"""
        ifv_id, tk_id, ucv_id = operators['ifv_id'], operators['tk_id'], operators['ucv_id']
        if not myAgent.get_bop(ifv_id):
            if not myAgent.get_bop(tk_id):
                if not myAgent.get_bop(ucv_id):
                    return False
        if not myAgent.situations.enemies:
            return False
        city_1_loc, city_2_loc = 4435, 4029
        city_1, city_2 = myAgent.get_city(city_1_loc), myAgent.get_city(city_2_loc)
        if city_1['flag'] != self.color and city_2['flag'] != self.color:
            return False
        if city_1['flag'] == self.color and city_2['flag'] == self.color:
            return True
        win_sore = myAgent.observation['scores']['red_win']
        if win_sore > 0:
            return True
        return False
    def check_red_control(self, operators, myAgent):
        """红方判断是否实施夺控任务"""
        ifv_id, tk_id, ucv_id = operators['ifv_id'], operators['tk_id'], operators['ucv_id']
        if not myAgent.get_bop(ifv_id):
            if not myAgent.get_bop(tk_id):
                if not myAgent.get_bop(ucv_id):
                    return False
        if not myAgent.situations.enemies:
            return True
        no_vehicles_enemy = True
        for enemy_info in myAgent.situations.enemies:
            if enemy_info['operator']['type'] == BopType.Vehicle:
                no_vehicles_enemy = False
        if no_vehicles_enemy:  # 敌方没有车辆单位，直接夺控
            return True
        # 根据到夺控点的机动时间，判断是否夺控
        map_size = myAgent.get_map_size()
        city_1_loc, city_2_loc = 4435, 4029
        city_1, city_2 = myAgent.get_city(city_1_loc), myAgent.get_city(city_2_loc)
        if city_1['flag'] == self.color and city_2['flag'] == self.color:
            return False
        city_1_loc_ind = mapid_2_matind(city_1_loc, map_size)
        city_2_loc_ind = mapid_2_matind(city_2_loc, map_size)
        cost_matrix = myAgent.my_map['cost_matrix'][0]
        min_cost = 1000
        for opr in myAgent.situations.our_oprs:
            if opr['type'] == BopType.Vehicle:
                opr_loc_ind = mapid_2_matind(opr['cur_hex'], map_size)
                if city_1['flag'] != self.color:
                    move_cost = cost_matrix[city_1_loc_ind, opr_loc_ind]
                    if min_cost > move_cost:
                        min_cost = move_cost
                if city_2['flag'] != self.color:
                    move_cost = cost_matrix[city_2_loc_ind, opr_loc_ind]
                    if min_cost > move_cost:
                        min_cost = move_cost
        cur_step = myAgent.observation['time']['cur_step']
        if cur_step >= self.max_step - min_cost - 100:  # 100是松弛数值
            return True
        return False

    def check_red_load_infantry(self, operators, myAgent):
        """红方战车装载步兵任务的判断"""
        cm_1_id = operators['cm_1_id']
        cm_1 = myAgent.get_bop(cm_1_id)
        if not cm_1:
            return False
        if cm_1['get_off_remain_time'] > 0:
            return True

    def check_blue_deliver_infantry_1(self, operators, myAgent):
        """蓝方战车1输送步兵1任务的判断"""
        ifv_1_id, inf_1_id = operators['ifv_1_id'], operators['inf_1_id']
        if not myAgent.get_bop(ifv_1_id):
            return False
        inf = myAgent.get_bop(inf_1_id)
        if not inf:
            return False
        if inf['get_on_remain_time'] > 0:
            return True

    def check_blue_deliver_infantry_2(self, operators, myAgent):
        """蓝方战车1输送步兵1任务的判断"""
        ifv_2_id, inf_2_id = operators['ifv_2_id'], operators['inf_2_id']
        if not myAgent.get_bop(ifv_2_id):
            return False
        inf = myAgent.get_bop(inf_2_id)
        if not inf:
            return False
        if inf['get_on_remain_time'] > 0:
            return True

    def check_red_unload_infantry(self, operators, myAgent):
        """红方战车卸载步兵任务的判断"""
        ifv_id, inf_id = operators['ifv_id'], operators['inf_id']
        if not myAgent.get_bop(ifv_id):
            return False
        inf = myAgent.get_bop(inf_id)
        if not inf:
            return False
        if ifv_id in self.task_copy.keys():
            if self.task_copy[ifv_id][0] == 'unload':
                return False
        if inf['on_board'] == 1:
            return True

    def check_blue_anti_control(self, myAgent):
        # 反夺控任务的判断
        last_observation = self.last_observation
        for last_city in last_observation['cities']:
            if last_city['flag'] != self.color:
                city = myAgent.get_city(last_city['coord'])
                if city['flag'] == self.color:  # 成功夺控
                    return True
        return False

    def check_blue_ambush_ifv_1(self, operators, myAgent):
        """ 判断蓝方战车1是否执行“伏击”任务"""
        ifv_id = operators['ifv_1_id']
        inf_id = operators['inf_1_id']
        ifv = myAgent.get_bop(ifv_id)
        inf = myAgent.get_bop(inf_id)
        # 战车算子已死亡
        if not ifv:
            return False
        # 如果战车有需要执行的任务
        if ifv['obj_id'] in self.task_copy.keys():
            if self.task_copy[ifv_id][0] not in ['Success', 'Failure', 'Error']:
                return False
        # 如果步兵算子还在车上
        if inf:
            if inf['get_off_remain_time'] > 0:
                return True
        return False

    def check_blue_ambush_ifv_2(self, operators, myAgent):
        """ 判断蓝方战车1是否执行“伏击”任务"""
        ifv_id = operators['ifv_2_id']
        inf_id = operators['inf_2_id']
        ifv = myAgent.get_bop(ifv_id)
        inf = myAgent.get_bop(inf_id)
        # 战车算子已死亡
        if not ifv:
            return False
        # 如果战车有需要执行的任务
        if ifv['obj_id'] in self.task_copy.keys():
            if self.task_copy[ifv_id][0] not in ['Success', 'Failure', 'Error']:
                return False
        # 如果步兵算子还在车上
        if inf:
            if inf['get_off_remain_time'] > 0:
                return True
        return False

    def check_red_ifv_ambush(self, operators, myAgent):
        ifv_id, inf_id, ucv_id = operators['ifv_id'], operators['inf_id'], operators['ucv_id']
        ifv_obj, inf_obj, ucv_obj = myAgent.get_bop(ifv_id), myAgent.get_bop(inf_id), myAgent.get_bop(ucv_id)
        # 战车算子已死亡
        if not ifv_obj:
            return False
        # 如果战车正在执行“伏击”任务
        if ifv_obj['obj_id'] in self.task_copy.keys():
            if self.task_copy[ifv_obj['obj_id']][0] == 'ambush2nd':
                return False
        # 没有可用的步兵或无人战车
        if not inf_obj:
            no_used_inf = True
        elif inf_obj['obj_id'] not in self.task_copy.keys():
            no_used_inf = True
        elif self.task_copy[inf_obj['obj_id']][0] != 'ambush2nd':
            no_used_inf = True
        else:
            no_used_inf = False
        if not ucv_obj:
            no_used_ucv = True
        elif ucv_obj['obj_id'] not in self.task_copy.keys():
            no_used_ucv = True
        elif self.task_copy[ucv_obj['obj_id']][0] != 'ambush2nd':
            no_used_ucv = True
        else:
            no_used_ucv = False
        if no_used_inf and no_used_ucv:
            return False
        # 如果步兵算子刚下车
        last_inf = myAgent.get_bop_lastobservation(inf_id)
        if last_inf['on_board'] == 1 and inf_obj['on_board'] == 0:
            return True

    def check_launch_2nd_cm(self, operators, myAgent):
        """释放第二个巡飞弹的判断"""
        ifv_id, cm_1_id, cm_2_id = operators['ifv_id'], operators['cm_1_id'], operators['cm_2_id']
        cm_1 = myAgent.get_bop(cm_1_id)
        if cm_1:
            return False
        ifv, cm_2 = myAgent.get_bop(ifv_id), myAgent.get_bop(cm_2_id)
        if not ifv:
            return False
        if not cm_2:
            return False
        if cm_2['on_board'] == 0:
            return False
        if cm_2['get_off_remain_time'] > 0:
            return False
        return True

    def check_red_fire_propulsion_tank(self, operators, myAgent):
        """坦克开始“火力推进”任务的判断"""
        if not myAgent.situations.enemies:
            return False
        tk_id = operators['tk_id']
        tk = myAgent.get_bop(tk_id)
        if not tk:
            return False
        cur_step = myAgent.observation['time']['cur_step']
        if cur_step >= 800:
            return True
        if tk['remain_bullet_nums'][BulletType.HeavyMissile] <= 0:
            return True

    def check_red_fire_propulsion_ifv(self, operators, myAgent):
        """红方战车开始“火力推进”任务的判断"""
        if not myAgent.situations.enemies:
            return False
        ifv_id, ucv_id = operators['ifv_id'], operators['ucv_id']
        ifv, ucv = myAgent.get_bop(ifv_id), myAgent.get_bop(ucv_id)
        if not ifv:
            return False
        if ucv and ifv['remain_bullet_nums'][BulletType.HeavyMissile] > 0:  # 有无人战车，有导弹，实施引导射击
            return False
        else:
            cur_step = myAgent.observation['time']['cur_step']
            if ifv['remain_bullet_nums'][BulletType.HeavyMissile] <= 0 or cur_step < 1000:  # 1000s内主要在5439打伏击
                return False
        return True

    def check_red_support_firepower(self, operators, myAgent):
        """红方战车开始“火力支援”任务的判断"""
        cur_step = myAgent.observation['time']['cur_step']
        if cur_step < 1000:  # 1000s内主要在5439打伏击
            return False
        enemies = myAgent.situations.enemies
        no_vehicle = True
        for enemy_info in enemies:
            if enemy_info['operator']['type'] == BopType.Vehicle:
                no_vehicle = False
                break
        if no_vehicle:
            return False
        ifv_id, ucv_id = operators['ifv_id'], operators['ucv_id']
        ifv, ucv = myAgent.get_bop(ifv_id), myAgent.get_bop(ucv_id)
        if not ifv:
            return False
        if not ucv:
            return False
        if ifv['remain_bullet_nums'][BulletType.HeavyMissile] <= 0:
            return False
        map_size = myAgent.get_map_size()
        n_hexes = map_size['row'] * map_size['col']
        dist_matrix = myAgent.my_map['dist_matrix']
        ifv_loc = ifv['cur_hex']
        ifv_loc_ind = mapid_2_matind(ifv_loc, map_size)
        enemy_locs_bool = np.zeros(n_hexes, dtype=bool)
        for enemy_info in enemies:
            if enemy_info['operator']['type'] == BopType.Vehicle:
                pos_probs = enemy_info['pos_probs']
                enemy_locs_bool[pos_probs > 0] = True
        if np.min(dist_matrix[ifv_loc_ind, enemy_locs_bool]) <= 20:
            return False
        return True

    def check_red_guide_fire_ucv(self, operators, myAgent):
        """红方UCV开始“引导射击火力输出”任务的判断"""
        cur_step = myAgent.observation['time']['cur_step']
        if cur_step < 850:
            return False
        enemies = myAgent.situations.enemies
        no_vehicle = True
        for enemy_info in enemies:
            if enemy_info['operator']['type'] == BopType.Vehicle:
                no_vehicle = False
                break
        if no_vehicle:
            return False
        ifv_id, ucv_id = operators['ifv_id'], operators['ucv_id']
        ifv, ucv = myAgent.get_bop(ifv_id), myAgent.get_bop(ucv_id)
        if not ucv:
            return False
        if not ifv:
            return False
        if ifv['remain_bullet_nums'][BulletType.HeavyMissile] <= 0:
            return False
        return True

    def check_red_fire_propulsion_ucv(self, operators, myAgent):
        """红方UCV开始“火力推进”任务的判断"""
        if not myAgent.situations.enemies:
            return False
        ucv_id = operators['ucv_id']
        ucv = myAgent.get_bop(ucv_id)
        if not ucv:
            return False
        cur_step = myAgent.observation['time']['cur_step']
        if cur_step >= 850:
            return True

    def check_blue_control_infantry_1(self, operators, myAgent):
        """蓝方步兵1开始“夺控”任务的判断"""
        inf_id = operators['inf_1_id']
        inf = myAgent.get_bop(inf_id)
        if not inf:
            return False
        if inf['get_off_remain_time'] > 0:
            return True
        return False

    def check_blue_ambush_infantry_2(self, operators, myAgent):
        """蓝方步兵2开始“伏击”任务的判断"""
        inf_id = operators['inf_2_id']
        inf = myAgent.get_bop(inf_id)
        if not inf:
            return False
        if inf['get_off_remain_time'] > 0:
            return True
        return False


def get_enemy_directions(refer_loc, enemies):
    """2020-6-23
    获得当前时刻，敌方算子相对于refer_loc六角格的方向
    """
    directions = []
    for enemy_info in enemies:
        enemy_loc = enemy_info['operator']['cur_hex']
        directions.append(get_direction(refer_loc, enemy_loc))
    return directions
